package impl

import (
	"context"
	"encoding/json"
	"errors"
	"gorm.io/gorm"
	"time"
	"wms/entity"
	"wms/glo"
	"wms/pb"
	"wms/service/convert"
	"wms/service/model"
	"wms/service/request"
	"wms/util"
)

type StockService struct {
}

var StockSrvImpl StockService

func (s *StockService) SkuList(ctx context.Context, arg *request.QueryStockSkuListRequest) ([]*model.SkuStock, error) {
	var skuStockEntities []*entity.SkuStock
	_db := glo.Db_wms.Model(&entity.SkuStock{})
	if arg.StockStatus != nil {
		_db = _db.Where("stock_status = ?", *arg.StockStatus)
	}
	if err := _db.Find(&skuStockEntities).Error; err != nil {
		return nil, errors.New("sql err" + err.Error())
	}

	modelListOut := make([]*model.SkuStock, 0, len(skuStockEntities))
	for _, tmp := range skuStockEntities {
		_w := new(model.SkuStock)
		_w.Mac = tmp.Mac
		_w.Iccid = tmp.Iccid
		_w.StockStatus = tmp.StockStatus
		_w.WarehouseID = tmp.WarehouseID
		_w.SkuCode = tmp.SkuCode
		_w.StockInAt = tmp.StockInAt.Format(time.DateTime)
		modelListOut = append(modelListOut, _w)
	}
	return modelListOut, nil
}

func (s *StockService) Statistics(ctx context.Context, arg *request.QueryStockStatisticsRequest) (*model.StockStatistics, error) {
	var err error
	var viewStock []*entity.ViewStockCount
	_db := glo.Db_wms.Model(&entity.ViewStockCount{}).Find(&viewStock)
	err = _db.Find(&viewStock).Error
	if err != nil {
		return nil, err
	}

	r := new(model.StockStatistics)
	r.Raw = viewStock
	return r, nil
}

func (s *StockService) InAddApply(ctx context.Context, arg *request.AddStockInApplyRequest) error {
	var err error
	var data []byte
	data, err = json.Marshal(arg)
	if err != nil {
		return err
	}
	util.Log("req json:\n" + string(data) + "\n")
	// skuType":"IIG3000-4G" 查询对应的sku id
	err = s.BeforeAddInApply(ctx, arg)
	if err != nil {
		return err
	}
	// 插入数据库
	addEntity := new(entity.StockIn)
	argStockForm := arg.StockForm
	util.CopyStruct(addEntity, &argStockForm)
	addEntity.AuditStatus = pb.AuditStatus_AUTH
	skuListBytes, _ := json.Marshal(arg.SkuList)
	addEntity.SkuList = skuListBytes

	//
	resultDb := glo.Db_wms.Create(addEntity)
	if resultDb.Error != nil {
		return errors.New("insert stock in form err:" + resultDb.Error.Error())
	}

	return nil
}

func (s *StockService) OutAddApply(ctx context.Context, arg *request.AddStockOutApplyRequest) error {
	var err error
	var data []byte
	data, err = json.Marshal(arg)
	if err != nil {
		return err
	}
	util.Log("req json:\n" + string(data) + "\n")
	err = s.BeforeAddOutApply(ctx, arg)
	if err != nil {
		return err
	}
	// 插入数据库
	addEntity := new(entity.StockOut)
	argStockForm := arg.StockForm
	util.CopyStruct(addEntity, &argStockForm)
	addEntity.AuditStatus = pb.AuditStatus_AUTH
	skuListBytes, _ := json.Marshal(arg.SkuList)
	addEntity.SkuList = skuListBytes
	//
	resultDb := glo.Db_wms.Create(addEntity)
	if resultDb.Error != nil {
		return errors.New("insert stock out form err:" + resultDb.Error.Error())
	}
	return nil
}

func (s *StockService) BeforeAddInApply(ctx context.Context, arg *request.AddStockInApplyRequest) error {
	// skuType":"IIG3000-4G" 查询对应的sku id
	stockInNo := arg.StockForm.StockInNo
	if len(stockInNo) == 0 {
		return errors.New("入库单号未空")
	}
	var stockInCount int64
	_ = glo.Db_wms.Model(&entity.StockIn{}).Where("stock_in_no = ?", stockInNo).Count(&stockInCount)
	if stockInCount > 0 {
		return errors.New("入库单号已存在, 数据重复")
	}
	// 业务待定
	return nil
}

func (s *StockService) BeforeAddOutApply(ctx context.Context, arg *request.AddStockOutApplyRequest) error {
	stockOutNo := arg.StockForm.StockOutNo
	if len(stockOutNo) == 0 {
		return errors.New("出库单号未空")
	}
	var stockOutCount int64
	_ = glo.Db_wms.Model(&entity.StockOut{}).Where("stock_out_no = ?", stockOutNo).Count(&stockOutCount)
	if stockOutCount > 0 {
		return errors.New("出库单号已存在, 数据重复")
	}
	// 业务待定
	// mac 仓库存在性校验
	return nil
}

func (s *StockService) InAudit(ctx context.Context, arg *request.AuditStockInRequest) error {
	// TODO redis 锁
	_ = context.TODO()

	// 查询是否存在, 判断待审核状态
	var stockIn entity.StockIn
	resultDb := glo.Db_wms.First(&stockIn, arg.StockInID)
	if errors.Is(resultDb.Error, gorm.ErrRecordNotFound) {
		util.Logf("AuditIn record not found id:%d", arg.StockInID)
		return errors.New("数据不存在")
	}
	if stockIn.AuditStatus != pb.AuditStatus_AUTH {
		return errors.New("非待审核状态, 不能审核")
	}

	// 获取仓库的id
	// 优化到内存
	var warehouse entity.Warehouse
	// db.First(&user, "id = ?", "1b74413f-f3b8-409f-ac47-e8c062e3472a")
	resultDb = glo.Db_wms.First(&warehouse, "warehouse_name = ?", stockIn.StockInWarehouse)
	if errors.Is(resultDb.Error, gorm.ErrRecordNotFound) {
		util.Logf("warehouse record not found id:%d", arg.StockInID)
		return errors.New("仓库数据不存在")
	}
	//
	var stockApplyTimeline entity.StockApplyTimeline
	stockApplyTimeline.StockApplyType = pb.StockApplyType_APPLY_TYPE_IN
	stockApplyTimeline.StockInNo = stockIn.StockInNo
	if arg.AuditStatus == pb.AuditStatus_PASS {
		stockApplyTimeline.AuditStatus = pb.AuditStatus_PASS
		stockApplyTimeline.PassRemark = arg.Remark
	} else if arg.AuditStatus == pb.AuditStatus_DENY {
		stockApplyTimeline.AuditStatus = pb.AuditStatus_DENY
		stockApplyTimeline.DenyRemark = arg.Remark
	} else {
		return errors.New("AuditStatus参数异常")
	}
	//
	auditAt := time.Now()
	txErr := glo.Db_wms.Transaction(func(tx *gorm.DB) error {
		// 多表业务
		// tx.Model(&stockIn).Update("audit_status", arg.AuditStatus)
		updateMap := map[string]any{"audit_status": arg.AuditStatus, "audit_at": auditAt}
		if err := tx.Model(&stockIn).Updates(updateMap).Error; err != nil {
			return err
		}
		if err := tx.Create(&stockApplyTimeline).Error; err != nil {
			return err
		}
		return nil
	})
	if txErr != nil {
		return errors.New("事务异常:" + txErr.Error())
	}

	if arg.AuditStatus == pb.AuditStatus_PASS {
		// 计算统计
		go func() {
			warehouseId := warehouse.WarehouseID
			skuSlice := make([]*model.StockItem, 0)
			err := json.Unmarshal(stockIn.SkuList, &skuSlice)
			if err != nil {
				util.Logf("json unmarshal err:" + err.Error())
				return
			}
			if len(skuSlice) == 0 {
				return
			}
			macArr, macMap := convert.StockItemSliceToMap(skuSlice)

			for _, tmpMac := range macArr {
				stockItem := macMap[tmpMac]
				if stockItem == nil { // 空指针
					continue
				}
				var isInsert, isUpdate bool
				var entitySkuStock entity.SkuStock
				var entityLogStock entity.LogStock
				resultDb = glo.Db_wms.First(&entitySkuStock, "mac = ?", tmpMac)
				if err == nil {
					isUpdate = true
				} else if errors.Is(resultDb.Error, gorm.ErrRecordNotFound) {
					isInsert = true
				} else {
					// 其他异常
					util.Logf("InAudit,err:" + err.Error())
					continue
				}
				//
				entitySkuStock.StockStatus = pb.StockStatus_IN
				entitySkuStock.Mac = tmpMac
				entitySkuStock.Iccid = stockItem.Iccid
				entitySkuStock.SkuCode = stockItem.SkuType
				entitySkuStock.WarehouseID = warehouseId
				entitySkuStock.StockInAt = &auditAt
				// 补充sql日志数据
				entityLogStock.StockStatus = pb.StockApplyType_APPLY_TYPE_IN
				entityLogStock.StockInID = stockIn.StockInID
				entityLogStock.StockInNo = stockIn.StockInNo
				entityLogStock.WarehouseID = warehouseId
				entityLogStock.Reviewer = stockIn.Reviewer
				entityLogStock.StockInAt = &auditAt
				entityLogStock.Mac = tmpMac
				entityLogStock.Iccid = stockItem.Iccid
				entityLogStock.SkuID = -1
				entityLogStock.SkuCode = stockItem.SkuType

				txErr = glo.Db_wms.Transaction(func(tx *gorm.DB) error {
					// 多表业务
					//
					if isInsert {
						if err = tx.Create(&entitySkuStock).Error; err != nil {
							return err
						}
					}
					if isUpdate {
						if err = tx.Save(&entitySkuStock).Error; err != nil {
							return err
						}
					}
					// 写日志
					if err = tx.Create(&entityLogStock).Error; err != nil {
						return err
					}
					return nil
				})
				if txErr != nil {
					util.Logf("统计,txErr:" + txErr.Error())
				}
			}
		}()
	}

	return nil
}

func (s *StockService) OutAudit(ctx context.Context, arg *request.AuditStockOutRequest) error {
	// TODO redis 锁
	_ = context.TODO()

	// 查询是否存在, 判断待审核状态
	var stockOut entity.StockOut
	resultDb := glo.Db_wms.First(&stockOut, arg.StockOutID)
	if errors.Is(resultDb.Error, gorm.ErrRecordNotFound) {
		util.Logf("Audit Out record not found id:%d", arg.StockOutID)
		return errors.New("出库单数据不存在")
	}
	if stockOut.AuditStatus != pb.AuditStatus_AUTH {
		return errors.New("非待审核状态, 不能审核")
	}

	// 获取仓库的id
	// TODO 是否校验

	//
	var stockApplyTimeline entity.StockApplyTimeline
	stockApplyTimeline.StockApplyType = pb.StockApplyType_APPLY_TYPE_OUT
	stockApplyTimeline.StockOutNo = stockOut.StockOutNo
	if arg.AuditStatus == pb.AuditStatus_PASS {
		stockApplyTimeline.AuditStatus = pb.AuditStatus_PASS
		stockApplyTimeline.PassRemark = arg.Remark
	} else if arg.AuditStatus == pb.AuditStatus_DENY {
		stockApplyTimeline.AuditStatus = pb.AuditStatus_DENY
		stockApplyTimeline.DenyRemark = arg.Remark
	} else {
		return errors.New("AuditStatus参数异常")
	}
	//
	auditAt := time.Now()
	txErr := glo.Db_wms.Transaction(func(tx *gorm.DB) error {
		// 多表业务
		updateMap := map[string]any{"audit_status": arg.AuditStatus, "audit_at": auditAt}
		if err := tx.Model(&stockOut).Updates(updateMap).Error; err != nil {
			return err
		}
		if err := tx.Create(&stockApplyTimeline).Error; err != nil {
			return err
		}
		return nil
	})
	if txErr != nil {
		return errors.New("事务异常:" + txErr.Error())
	}

	if arg.AuditStatus == pb.AuditStatus_PASS {
		// 计算统计
		go func() {

			skuSlice := make([]*model.StockItem, 0)
			err := json.Unmarshal(stockOut.SkuList, &skuSlice)
			if err != nil {
				util.Logf("json unmarshal err:" + err.Error())
				return
			}
			if len(skuSlice) == 0 {
				return
			}
			macArr, macMap := convert.StockItemSliceToMap(skuSlice)

			for _, tmpMac := range macArr {
				stockItem := macMap[tmpMac]
				if stockItem == nil { // 空指针
					continue
				}

				var entitySkuStock entity.SkuStock
				var entityLogStock entity.LogStock
				resultDb = glo.Db_wms.First(&entitySkuStock, "mac = ?", tmpMac)
				if err != nil {
					util.Logf("OutAudit,err:" + err.Error())
					continue
				}
				// 库存状态 入库改成出库
				entitySkuStock.StockStatus = pb.StockStatus_OUT
				entitySkuStock.StockOutAt = &auditAt
				// 补充sql日志数据
				entityLogStock.StockStatus = pb.StockApplyType_APPLY_TYPE_OUT
				entityLogStock.StockOutID = stockOut.StockOutID
				entityLogStock.StockOutNo = stockOut.StockOutNo
				entityLogStock.WarehouseID = entitySkuStock.WarehouseID
				entityLogStock.Reviewer = stockOut.Reviewer
				entityLogStock.StockOutAt = &auditAt
				entityLogStock.Mac = tmpMac
				entityLogStock.Iccid = stockItem.Iccid
				entityLogStock.SkuID = -1
				entityLogStock.SkuCode = stockItem.SkuType

				txErr = glo.Db_wms.Transaction(func(tx *gorm.DB) error {
					// 多表业务
					if err = tx.Save(&entitySkuStock).Error; err != nil {
						return err
					}
					// 写日志
					if err = tx.Create(&entityLogStock).Error; err != nil {
						return err
					}
					return nil
				})
				if txErr != nil {
					util.Logf("统计,txErr:" + txErr.Error())
				}
			}
		}()
	}

	return nil
}

func (s *StockService) InDetail(ctx context.Context, arg *request.QueryStockInRequest) (*pb.StockIn, error) {
	if *arg.StockInID == 0 {
		return nil, errors.New("参数异常,id为0")
	}

	// redis里面读取缓存 proto.Marshal()

	entityStockIn := new(entity.StockIn)
	resultDb := glo.Db_wms.First(entityStockIn, *arg.StockInID)
	if resultDb.Error != nil {
		return nil, errors.New("查询异常:" + resultDb.Error.Error())
	}

	stockIn := new(pb.StockIn)
	util.CopyStruct(stockIn, entityStockIn)
	err := json.Unmarshal(entityStockIn.SkuList, &stockIn.SkuListJson)
	if err != nil {
		return nil, errors.New("查询异常:" + err.Error())
	}

	if int(entityStockIn.AuditStatus) > int(pb.AuditStatus_AUTH) {
		var entityStockApplyTimeline []*entity.StockApplyTimeline
		resultDb = glo.Db_wms.Where("stock_in_no = ?", entityStockIn.StockInNo).Find(&entityStockApplyTimeline)
		if resultDb.Error != nil {
			util.Logf("select apply time line,stock_in_no %s;err:%s", entityStockIn.StockInNo, resultDb.Error.Error())
		}
		if len(entityStockApplyTimeline) > 0 {
			stockInApplyTimeline := make([]*pb.StockInApplyTimeline, 0, len(entityStockApplyTimeline))
			for _, tmp := range entityStockApplyTimeline {
				_w := new(pb.StockInApplyTimeline)
				util.CopyStruct(_w, tmp)
				stockInApplyTimeline = append(stockInApplyTimeline, _w)
			}
			stockIn.ApplyTimeline = stockInApplyTimeline
		}
	}
	return stockIn, nil
}

func (s *StockService) OutDetail(ctx context.Context, arg *request.QueryStockOutRequest) (*model.StockOut, error) {
	if *arg.StockOutID == 0 {
		return nil, errors.New("参数异常,id为0")
	}
	entityStockOut := new(entity.StockOut)
	resultDb := glo.Db_wms.First(entityStockOut, *arg.StockOutID)
	if resultDb.Error != nil {
		return nil, errors.New("查询异常:" + resultDb.Error.Error())
	}

	stockOut := new(model.StockOut)
	util.CopyStruct(stockOut, entityStockOut)
	err := json.Unmarshal(stockOut.SkuList, &stockOut.SkuListJson)
	if err != nil {
		return nil, errors.New("查询异常:" + err.Error())
	}

	if int(entityStockOut.AuditStatus) > int(pb.AuditStatus_AUTH) {
		var entityStockApplyTimeline []*entity.StockApplyTimeline
		resultDb = glo.Db_wms.Where("stock_out_no = ?", entityStockOut.StockOutNo).Find(&entityStockApplyTimeline)
		if resultDb.Error != nil {
			util.Logf("select apply time line,stock_out_no %s;err:%s", entityStockOut.StockOutNo, resultDb.Error.Error())
		}
		if len(entityStockApplyTimeline) > 0 {
			stockOutApplyTimeline := make([]*model.StockOutApplyTimeline, 0, len(entityStockApplyTimeline))
			for _, tmp := range entityStockApplyTimeline {
				_w := new(model.StockOutApplyTimeline)
				util.CopyStruct(_w, tmp)
				stockOutApplyTimeline = append(stockOutApplyTimeline, _w)
			}
			stockOut.ApplyTimeline = stockOutApplyTimeline
		}
	}
	return stockOut, nil
}

func (s *StockService) InList(ctx context.Context, arg *request.QueryStockInRequest) ([]*model.StockIn, error) {
	var entities []*entity.StockIn
	_db := glo.Db_wms.Model(&entity.StockIn{})
	if arg.IsAuth != nil {
		_db = _db.Where("audit_status = ?", int(pb.AuditStatus_AUTH))
	}
	if err := _db.Order("stock_in_id desc").Find(&entities).Error; err != nil {
		return nil, errors.New("sql err" + err.Error())
	}
	//
	modelListOut := make([]*model.StockIn, 0, len(entities))
	for _, tmp := range entities {
		_w := new(model.StockIn)
		util.CopyStruct(_w, tmp)
		// _w.AuditStatus = tmp.AuditStatus
		_ = json.Unmarshal(_w.SkuList, &_w.SkuListJson)
		modelListOut = append(modelListOut, _w)
	}
	// 其他表 数据补充
	return modelListOut, nil
}

func (s *StockService) OutList(ctx context.Context, arg *request.QueryStockOutRequest) ([]*model.StockOut, error) {
	var entities []*entity.StockOut
	_db := glo.Db_wms.Model(&entity.StockOut{})
	if arg.IsAuth != nil {
		_db = _db.Where("audit_status = ?", int(pb.AuditStatus_AUTH))
	}
	if err := _db.Order("stock_out_id desc").Find(&entities).Error; err != nil {
		return nil, errors.New("sql err" + err.Error())
	}
	//
	modelListOut := make([]*model.StockOut, 0, len(entities))
	for _, tmp := range entities {
		_w := new(model.StockOut)
		util.CopyStruct(_w, tmp)
		_ = json.Unmarshal(_w.SkuList, &_w.SkuListJson)
		modelListOut = append(modelListOut, _w)
	}
	// 其他表 数据补充
	return modelListOut, nil
}
